package org.duang.http.core;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.duang.http.base.HttpRes;
import org.duang.http.common.ClientException;
import org.duang.http.util.HttpUtil;
import org.duang.kit.ToolsKit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DefaultServiceSSLClient {
	
	private static Logger logger = LoggerFactory.getLogger(DefaultServiceSSLClient.class);
	
	
	 public static HttpClient getSSLClient(boolean isSSL) {
	        CloseableHttpClient client = null;
	        if(isSSL) {
	            try {
	                X509HostnameVerifier verifier = new X509HostnameVerifier() {
	                    public void verify(String host, SSLSocket ssl) throws IOException {
	                    }
	                    public void verify(String host, X509Certificate cert) throws SSLException {
	                    }
	                    public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
	                    }
	                    public boolean verify(String s, SSLSession sslSession) {
	                        return true;
	                    }
	                };
	                TrustManager[] tm = new TrustManager[]{new X509TrustManager() {
	                    public X509Certificate[] getAcceptedIssuers() {
	                        return null;
	                    }
	                    public void checkServerTrusted(X509Certificate[] chain, String authType)
	                            throws CertificateException {
	                    }
	                    public void checkClientTrusted(X509Certificate[] chain, String authType)
	                            throws CertificateException {
	                    }
	                }};
	                SSLContext sslContext = SSLContext.getInstance("SSL");
	                sslContext.init(null, tm, new SecureRandom());
	                client = HttpClients.custom().setSslcontext(sslContext).setHostnameVerifier(verifier).build();
	            } catch (NoSuchAlgorithmException e) {
	                e.printStackTrace();
	            } catch (KeyManagementException e) {
	                e.printStackTrace();
	            }
	        } else {
	            client = HttpClients.createDefault();
	        }
	        return client;
	    }
	
	private static CloseableHttpClient getSSLClient(String certPath,String certPass) {
		CloseableHttpClient httpSSLClient = null;
		try{
			if(ToolsKit.isEmpty(httpSSLClient)){
		        char[] paramChar = certPass.toCharArray();
		        KeyStore keyStore  = KeyStore.getInstance("PKCS12");
		        FileInputStream instream = new FileInputStream(certPath);
		        try {
		            keyStore.load(instream, paramChar);
		        } finally {
		            instream.close();
		        }
		        // Trust own CA and all self-signed certs
		        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, paramChar).build();
		        // Allow TLSv1 protocol only
		        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,new String[] { "TLSv1" },null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
		        httpSSLClient = org.apache.http.impl.client.HttpClients.custom().setSSLSocketFactory(sslsf).build();
			}
		}catch(Exception e) {
			logger.warn(e.getMessage(), e);
		}
		return httpSSLClient;
	}
	
	public static HttpRes httpSSLPost(String url, Map<String,String> header, Map<String,File> fileMap) throws Exception {
		CloseableHttpClient client = (CloseableHttpClient) getSSLClient(StringUtils.startsWithIgnoreCase(url, "HTTPS"));
		CloseableHttpResponse httpResponse = null;
		HttpEntity resEntity = null;
		HttpRes httpRes = new HttpRes();	
		try {
            HttpPost httpPost = new HttpPost(url);
            buildHeader(httpPost, header);
            for (Iterator<Entry<String,File>> it = fileMap.entrySet().iterator(); it.hasNext();) {
            	Entry<String,File> entry = it.next();
            	String key = entry.getKey();
            	File file = entry.getValue();
//            	FileEntity fileEntity = new FileEntity(file, ContentType.APPLICATION_OCTET_STREAM);
//            	httpPost.setEntity(fileEntity);
            	httpPost.setEntity(MultipartEntityBuilder.create().addBinaryBody(key, file, ContentType.APPLICATION_OCTET_STREAM, file.getName()).build());
            }
            httpResponse = client.execute(httpPost);
            resEntity = httpResponse.getEntity();
            if (resEntity != null) {
            	String result = EntityUtils.toString(resEntity);
          		try {
          			if(ToolsKit.isEmpty(httpResponse)) return null;
          			httpRes.setResult(result);
          			httpRes.setStatusCode(httpResponse.getStatusLine().getStatusCode());
          			Header[] headers = httpResponse.getAllHeaders();
          			if (ToolsKit.isNotEmpty(headers)) {
          				Map<String, String> resultHeaders = new HashMap<String, String>(headers.length);
          				for (Header headerItem : headers) {
          					resultHeaders.put(headerItem.getName(), headerItem.getValue());
          				}
          				HttpUtil.convertHeaderCharsetFromIso88591(resultHeaders);
          				httpRes.setHeaders(resultHeaders);
          			}
          			if(httpRes.getStatusCode() != 200){ 
          				Exception exception = new ClientException(result);
          				httpRes.setException(exception);
          			}
          		} catch (Exception e) {
          			httpRes.setResult(e.getMessage());
          			httpRes.setException(e);
          		}
            }
        } catch (Exception e) {
        	e.printStackTrace();
        	logger.warn(e.getMessage(), e);
        } finally {
            try {
            	if (resEntity != null) {
                   resEntity.consumeContent();
                 }
            	httpResponse.close();
                client.close();
            } catch (IOException e) {
            }
        }
		return httpRes;
	}
	
	   private static void buildHeader(HttpUriRequest request, Map<String,String> header) {
	        if (null != header && !header.isEmpty()) {
	            for (Iterator<Entry<String,String>> it = header.entrySet().iterator(); it.hasNext();) {
	            	Entry<String,String> entry = it.next();
	                request.addHeader(entry.getKey(), entry.getValue());
	            }
	        }
	    }
	
	@SuppressWarnings("deprecation")
	public static HttpRes httpSSLPost(String url, String param, String certPath,String certPass) throws Exception {
		CloseableHttpClient httpSSLClient = getSSLClient(certPath, certPass);
		if(ToolsKit.isEmpty(httpSSLClient)) throw new NullPointerException("httpSSLClient is null");		
        HttpPost httpPost = new HttpPost(url);
        String result = "";
        HttpResponse httpResponse  = null;
        try {	        
	        // 得指明使用UTF-8编码，否则到API服务器XML的中文不能被成功识别
        	StringEntity postEntity = new StringEntity(param, ContentType.create(ContentType.TEXT_XML.getMimeType(), Charset.forName("UTF-8"))); //(param, "UTF-8");
	        httpPost.addHeader("Content-Type", "text/xml;charset=UTF-8");
//	        httpPost.setHeaders(headers);
	        httpPost.setEntity(postEntity);
            httpResponse = httpSSLClient.execute(httpPost);
            if (httpResponse.getStatusLine().getStatusCode() == 200) {
                HttpEntity entity = httpResponse.getEntity();
                result = EntityUtils.toString(entity, "UTF-8");
            } else {
                System.out.println("HTTP POST CODE : " + httpResponse.getStatusLine().getStatusCode() + " URL=" + url);
            }
        } catch (Exception e) {
        	e.printStackTrace();
        	System.out.println(e.getMessage());
        	throw new Exception(e);
        } finally {
        	httpSSLClient.close();
        }
        
        final HttpRes response = new HttpRes();	
		try {
			if(ToolsKit.isEmpty(httpResponse)) return null;
			response.setResult(result);
			response.setStatusCode(httpResponse.getStatusLine().getStatusCode());
			Header[] headers = httpResponse.getAllHeaders();
			if (ToolsKit.isNotEmpty(headers)) {
				Map<String, String> resultHeaders = new HashMap<String, String>(headers.length);
				for (Header header : headers) {
					resultHeaders.put(header.getName(), header.getValue());
				}
				HttpUtil.convertHeaderCharsetFromIso88591(resultHeaders);
				response.setHeaders(resultHeaders);
			}
			if(response.getStatusCode() != 200){ 
				Exception exception = new ClientException(result);
				response.setException(exception);
			}
		} catch (Exception e) {
			response.setResult(e.getMessage());
			response.setException(e);
		}
        return response;
	}
//
//	@SuppressWarnings("unchecked")
//	protected <T> Future<HttpResponse> sendRequestImpl(Request request, HttpCallBack<T> callback) throws Exception {
//		if(ToolsKit.isEmpty(request) && ToolsKit.isEmpty(request.getContent())){
//			throw new ClientException("request or content is null");
//		}
//		HttpRequestBase httpRequest = HttpFactory.createHttpRequest(request);
//		System.out.println("############################SSL#############################");
//		HttpResponse response = httpSSLClient.execute(httpRequest);
//		Future<HttpResponse> future = new BasicFuture<HttpResponse>((FutureCallback<HttpResponse>) response);		
////		httpRequest.releaseConnection(); //这个不用开注释
//		return future;
//	}
//	
//	public void close() {
//		if (ToolsKit.isNotEmpty(httpSSLClient) && httpSSLClient instanceof CloseableHttpClient) {
//			try{
//				((CloseableHttpClient)httpSSLClient).close();
//			} catch (Exception e) {
//				logger.warn(e.getMessage(), e);
//			}
//		}
//	}
}
